package com.iwhalecloud.bss.kite.cucc.service.batch;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.iwhalecloud.bss.kite.cart.common.constant.KiteCartErrorConsts;
import com.ztesoft.common.logger.api.ZLogger;
import com.ztesoft.common.logger.factory.ZLoggerFactory;

/**
 * 线程池工具类（批量定时任务）
 *
 * @author Jonathan
 */
public class BatchJobThreadPoolUtil {

    private BatchJobThreadPoolUtil() {
    }

    private static volatile BatchJobThreadPoolUtil threadPool = null;

    private static final ZLogger LOG = ZLoggerFactory.getLogger(BatchJobThreadPoolUtil.class, "ThreadPoolUtil");

    private static final Integer CORE_POOL_SIZE = 2;

    private static final Integer MAX_CORE_POOL_SIZE = 16;

    private static final Integer POOL_SIZE_MULTIPLE = 2;

    private ExecutorService pool = null;

    private static final Lock LOCK = new ReentrantLock();

    /**
     * <ul>
     * <li>极限状态：针对计算密集型与I/O密集型，无限大和cpu线程数的2倍</li>
     * <li>暂定 1024，实际应该按照以下公式测算优化：</li>
     * <li>Nthreads = Ncpu * Ucpu * (1 + W/C)</li>
     * <li>Nthreads：最大线程数</li>
     * <li>Ncpu：cpu核心线程数</li>
     * <li>Ucpu：目标cpu使用率，0 < Ucpu < 1</li>
     * <li>W/C：等待时间与计算时间的比率</li>
     * </ul>
     */
    private static final Integer MAXIMUM_POOL_SIZE = 2048;

    private static final Long KEEP_ALIVE_TIME = 5000L;

    private static final String THREAD_POOL_NAME_FORMAT = "BatchJob-Pool-%d";

    public static BatchJobThreadPoolUtil getInstance() {
        if (threadPool == null) {
            synchronized (BatchJobThreadPoolUtil.class) {
                if (threadPool == null) {
                    threadPool = new BatchJobThreadPoolUtil();
                }
            }
        }
        return threadPool;
    }

    public ExecutorService getThreadPool() {
        /**
         * 初始空线程池
         */
        if (pool != null) {
            return pool;
        }
        try {
            LOCK.tryLock(3, TimeUnit.SECONDS);
            if (pool != null) {
                return pool;
            }
            /**
             * 生成线程池名称
             */
            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(THREAD_POOL_NAME_FORMAT)
                .build();
            /**
             * 获取主机cpu核心数
             */
            int availableProcessors = Runtime.getRuntime().availableProcessors();
            availableProcessors = availableProcessors > MAX_CORE_POOL_SIZE ? MAX_CORE_POOL_SIZE : availableProcessors;

            Integer corePoolSize = availableProcessors < CORE_POOL_SIZE ? CORE_POOL_SIZE
                : availableProcessors;
            /**
             * 初始化线程池
             *
             * @param corePoolSize 核心线程数，除非设置了允许核心线程超时，否则要保留在池中的线程数，即使它们是空闲的
             * @param maximumPoolSize 池中允许的最大线程数
             * @param keepAliveTime 当线程数大于核心时，多余的空闲线程在终止前等待新任务的最长时间
             * @param unit keepAliveTime 的时间单位
             * @param workQueue 用于在执行任务之前保存任务的队列，此队列将只保存由 execute 方法提交的 Runnable 任务。
             * @param threadFactory 执行器创建新线程时要使用的工厂
             * @throws IllegalArgumentException if one of the following holds:<br>
             *             {@code corePoolSize < 0}<br>
             *             {@code keepAliveTime < 0}<br>
             *             {@code maximumPoolSize <= 0}<br>
             *             {@code maximumPoolSize < corePoolSize}
             * @throws NullPointerException if {@code workQueue} or {@code threadFactory} is null
             */
            pool = new ThreadPoolExecutor(
                corePoolSize,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(POOL_SIZE_MULTIPLE * corePoolSize),
                namedThreadFactory);
        }
        catch (Exception e) {
            LOG.error(KiteCartErrorConsts.SYSTEM_ERROR.getErrorCode(), e,  "Exception at Creating Migration Thread Pools" + e.getCause());
        }
        finally {
            LOCK.unlock();
        }
        return pool;
    }
}
